package com.chhuang.tutool.socket

import android.util.Log
import com.swallowsonny.convertextlibrary.readByteArrayBE
import com.swallowsonny.convertextlibrary.toAsciiString
import com.swallowsonny.convertextlibrary.toHexString
import io.netty.bootstrap.Bootstrap
import io.netty.buffer.Unpooled
import io.netty.channel.ChannelHandlerContext
import io.netty.channel.ChannelInitializer
import io.netty.channel.ChannelOption
import io.netty.channel.SimpleChannelInboundHandler
import io.netty.channel.nio.NioEventLoopGroup
import io.netty.channel.socket.DatagramPacket
import io.netty.channel.socket.SocketChannel
import io.netty.channel.socket.nio.NioDatagramChannel
import java.net.InetSocketAddress
import java.nio.charset.Charset
import kotlin.experimental.and


class NettyUDP(private val ip: String, private val port: Int, private val localPort: Int,
               private val success:(nettyUDP: NettyUDP)->Unit,
               private val failed:()->Unit,
               private val received:(content: ByteArray, ip:String, port:Int)->Unit) {
    companion object{
        private const val TAG = "NettyUDP"

        private var nioEventLoopGroup: NioEventLoopGroup? = null
        private var channel: NioDatagramChannel? = null

        /**
         * ip to int
         */
        fun ip2IntConvert(ip: String): Int {
            val ipArr = ip.split("\\.").toTypedArray()
            if (ipArr.size != 4) {
                return -1
            }
            var x = 0
            for (i in ipArr.indices) {
                x = x shl 8 or Integer.valueOf(ipArr[i])
            }
            return x
        }

        /**
         * udp 点对点 ip
         * udp 广播 用 255.255.255.255
         * udp 组播 用 要计算局域网组播地址
         * 根据提供的ip获取组播地址
         */
        fun getBroadcastIp(ip: Int): String {
            return ((ip and 0xff).toString() + "." + (ip shr 8 and 0xff) + "."
                    + (ip shr 16 and 0xff) + ".255")
        }

        fun getBroadcastIp(ipStr: String): String {
            val ip = ip2IntConvert(ipStr)
            return getBroadcastIp(ip)
        }
    }

    private var run: Boolean =false

    init {
        try {
            nioEventLoopGroup = NioEventLoopGroup()
            val bootstrap = Bootstrap()
            bootstrap.group(nioEventLoopGroup)
                .channel(NioDatagramChannel::class.java)
                .option(ChannelOption.SO_BROADCAST, true)
                .handler(object : ChannelInitializer<NioDatagramChannel>(){
                    override fun initChannel(ch: NioDatagramChannel?) {
                        ch?.pipeline()?.addLast(object : SimpleChannelInboundHandler<DatagramPacket>(){
                            override fun messageReceived(ctx: ChannelHandlerContext?, msg: DatagramPacket?) {
                                msg?.let {
                                    val remote = it.sender()
                                    val ip = remote.address.hostAddress!!
                                    val rport = remote.port
                                    Log.e(TAG, "messageReceived:${ip}:${rport} 发来的消息接收成功")

                                    val buffer = it.content()
                                    val len = buffer.readableBytes() //数据长度
                                    val datas = ByteArray(len)
                                    buffer.readBytes(datas) // 接收的数据

                                    val content: String = datas.toHexString()
                                    Log.e(TAG, """broadcast: received msg = ${content}""")
                                    val ascii: String = datas.toAsciiString()
                                    Log.e(TAG, """broadcast: received ascii msg = ${ascii}""")

                                    val utf8: String = String(datas, Charsets.UTF_8)
                                    Log.e(TAG, """broadcast: received ascii msg = ${utf8}""")
                                    val gbk: String = String(datas, Charset.forName("GBK"))
                                    Log.e(TAG, """broadcast: received ascii msg = ${gbk}""")

                                    received(datas, ip, port)
                                }
                            }
                        })
                    }

                })

            channel = bootstrap.bind(localPort).addListener {
                if(it.isSuccess){
                    Log.e(TAG, "doBroadcast: 绑定成功")
                    run = true
                    success(this)
                }else{
                    Log.e(TAG, "doBroadcast: 绑定失败")
                    failed()
                }
            }.sync().channel() as NioDatagramChannel

            channel?.closeFuture()?.await()

        }catch (ex: Exception){
            ex.printStackTrace()
            failed()
            nioEventLoopGroup?.shutdownGracefully()
        }
    }

    fun isRuning(): Boolean{
        return run
    }


    /**
     * 广播信息
     */
    fun doBroadcast(data: ByteArray){
        channel?.writeAndFlush(
            DatagramPacket(
                Unpooled.copiedBuffer(data),
                InetSocketAddress(ip, port)
            )
        )
    }

    /**
     * 销毁
     */
    fun destroy() {
        if(isRuning()) {
            channel?.close()
            nioEventLoopGroup?.shutdownGracefully()
            channel = null
            run = false
        }
    }

    /**
     * 获取本地ip 与 port
     */
    fun getLocalSocketAddress(): InetSocketAddress?{
        return if(isRuning()){
            channel?.localAddress()
        }else {
            null
        }
    }
}